package com.pointstar.dp.modules.project.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.Optional;

/**
 * @author 朱科睿
 * @since 1.5 2021/9/30
 **/
@Slf4j
@Configuration
public class JacksonCustomerConfig {

    /**
     * 时间格式
     */
    public static final String TIME_FORMAT = "HH:mm";

    /**
     * 适配自定义序列化和反序列化策略
     */
    @Bean
    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer())
                .serializerByType(LocalDate.class, new LocalDateSerializer())
                .serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(TIME_FORMAT, Locale.SIMPLIFIED_CHINESE)))
                .deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer())
                .deserializerByType(LocalDate.class, new LocalDateDeserializer())
                .deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(TIME_FORMAT, Locale.SIMPLIFIED_CHINESE)));
    }

    /**
     * {@link LocalDateTime}序列化为毫秒级时间戳
     */
    public static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {

        @Override
        public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) {
            ZoneId systemDefaultZone = ZoneId.systemDefault();
            Optional.ofNullable(value).ifPresent(v -> {
                long timestamp = v.atZone(systemDefaultZone).toInstant().toEpochMilli();
                try {
                    gen.writeNumber(timestamp);
                } catch (IOException e) {
                    log.error("【LocalDateTime序列化为毫秒级时间戳异常】入参：{}，系统默认时区：{}。", timestamp, systemDefaultZone, e);
                }
            });
        }

    }

    /**
     * {@link LocalDate}序列化为毫秒级时间戳
     */
    public static class LocalDateSerializer extends JsonSerializer<LocalDate> {

        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) {
            ZoneId systemDefaultZone = ZoneId.systemDefault();
            Optional.ofNullable(value).ifPresent(v -> {
                long timestamp = v.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
                try {
                    gen.writeNumber(timestamp);
                } catch (IOException e) {
                    log.error("【LocalDate序列化为毫秒级时间戳异常】入参：{}，系统默认时区：{}。", timestamp, systemDefaultZone, e);
                }
            });
        }

    }

    /**
     * 毫秒级时间戳反序列化为{@link LocalDateTime}
     */
    public static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {

        @Override
        public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext) {
            long timestamp = 0;
            ZoneId systemDefaultZone = ZoneId.systemDefault();
            try {
                timestamp = p.getValueAsLong();
                return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), systemDefaultZone);
            } catch (IOException e) {
                log.error("【毫秒级时间戳反序列化为LocalDateTime】入参：{}，系统默认时区：{}。", timestamp, systemDefaultZone, e);
            }
            return null;
        }

    }

    /**
     * 毫秒级时间戳反序列化为{@link LocalDate}
     */
    public static class LocalDateDeserializer extends JsonDeserializer<LocalDate> {

        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext deserializationContext) {
            long timestamp = 0;
            ZoneId systemDefaultZone = ZoneId.systemDefault();
            try {
                timestamp = p.getValueAsLong();
                return LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp), systemDefaultZone).toLocalDate();
            } catch (IOException e) {
                log.error("【毫秒级时间戳反序列化为LocalDate】入参：{}，系统默认时区：{}。", timestamp, systemDefaultZone, e);
            }
            return null;
        }

    }

}
